import numpy as nm


# 从全量偏好矩阵里，基于用户的度计算出稀疏增强图
# 主要就是统计出度数在中位数以下的项目和用户结点
# 把它们对应行/列里，非零的值变成指定小数参数
# 这样就等于增加了稀疏结点的交互记录，同时不影响本来的记录
def sparse_graph_augment(num_of_item, num_of_user, liked_graph):
    # 统计用户和项目的度，即用户看过的项目，和项目被看过的用户数量
    item_degree_vector = nm.zeros([num_of_item])
    user_degree_vector = nm.zeros([num_of_user])

    # 求每个项目的度，存在对应向量里
    for item_id in range(num_of_item):
        item_degree_vector[item_id] = liked_graph[item_id, :].sum()

    # 求每个用户的度，存在对应向量里
    for user_id in range(num_of_user):
        user_degree_vector[user_id] = liked_graph[:, user_id].sum()

    # 这里计算下项目的度的中位数
    # 然后把低于中位数（不含中位数）的项目id记录下来
    # 存进item_ids_under_middle
    item_ids_under_middle = []
    item_degree_middle = nm.median(item_degree_vector)
    for item_id in range(num_of_item):
        if item_degree_vector[item_id] < item_degree_middle:
            item_ids_under_middle.append(item_id)
    # 同理计算下用户的度的中位数
    # 然后把低于中位数（不含中位数）的用户id记录下来
    # 存进user_ids_under_middle
    user_ids_under_middle = []
    user_degree_middle = nm.median(user_degree_vector)
    for user_id in range(num_of_user):
        if user_degree_vector[user_id] < user_degree_middle:
            user_ids_under_middle.append(user_id)
    # 对liked_graph进行遍历
    # 如果行id属于item_ids_under_middle，则该行的值里，0值变成参数D
    # 如果列id属于user_ids_under_middle，则该列的值里，0值变成参数D
    # 得到的结果存在一个新矩阵里，sparse_liked_graph
    D = 0.7
    sparse_liked_graph = liked_graph.copy()
    for item_id in range(num_of_item):
        if item_id not in item_ids_under_middle:
            for user_id in range(num_of_user):
                if sparse_liked_graph[item_id, user_id] == 0:
                    sparse_liked_graph[item_id][user_id] = D
    for user_id in range(num_of_user):
        if user_id not in user_ids_under_middle:
            for item_id in range(num_of_item):
                if sparse_liked_graph[item_id, user_id] == 0:
                    sparse_liked_graph[item_id][user_id] = D

    sparse_liked_graph_nozero_count = nm.count_nonzero(sparse_liked_graph)
    print('sparse_liked_graph非0元素：', sparse_liked_graph_nozero_count)

    return sparse_liked_graph


# 基于稀疏图计算出资源分配矩阵
def sparse_weights_compute(num_of_item, num_of_user, sparse_liked_graph):
    # ******************稀疏子图分配矩阵的计算*****************
    # 按照前边基于random_augment_liked_graph计算出weights的操作
    # 在sparse_liked_graph上进行相同的计算过程，得到sparse_weights

    # 统计稀疏用户和项目的度，即用户看过的项目，和项目被看过的用户数量
    sparse_item_degree_vector = nm.zeros([num_of_item])
    sparse_user_degree_vector = nm.zeros([num_of_user])

    # 注意核心的变量名都要改成sparse开头以示区分
    # 后续的变量处理同这里一样

    # 求稀疏项目的度，存在对应向量里
    for item_id in range(num_of_item):
        sparse_item_degree_vector[item_id] = sparse_liked_graph[item_id, :].sum()
    # 求稀疏用户的度，存在对应向量里
    for user_id in range(num_of_user):
        sparse_user_degree_vector[user_id] = sparse_liked_graph[:, user_id].sum()

    # 计算每个用户未交互项目的度
    sparse_user_interact_item_num_total = nm.ones(num_of_user)
    sparse_user_interact_item_num_total *= num_of_item
    sparse_user_not_interact_item_num = sparse_user_interact_item_num_total - sparse_user_degree_vector

    # 为防止之后的除法出现0，手动将0值改为极大值
    for item_id in range(num_of_item):
        if sparse_item_degree_vector[item_id] == 0.0:
            sparse_item_degree_vector[item_id] = 99999
    for user_id in range(num_of_user):
        if sparse_user_degree_vector[user_id] == 0.0:
            sparse_user_degree_vector[user_id] = 99999

    # 求稀疏资源配额矩阵
    sparse_weights = nm.zeros([num_of_item, num_of_item])
    # 对稀疏喜欢图进行转置
    sparse_liked_graph_t = sparse_liked_graph.transpose()
    sparse_temp = nm.zeros([num_of_user, num_of_item])
    # 注意这里的变量也要换成稀疏的
    for i in range(num_of_item):
        sparse_temp[:, i] = sparse_liked_graph_t[:, i] / sparse_user_degree_vector
    sparse_temp = nm.dot(sparse_liked_graph, sparse_temp)
    for i in range(num_of_item):
        sparse_weights[i, :] = sparse_temp[i, :] / sparse_item_degree_vector

    sparse_weights_nozero_count = nm.count_nonzero(sparse_weights)
    print('sparse_weights非0元素：', sparse_weights_nozero_count)

    return sparse_weights
